package entity;

import java.io.Serializable;
import java.util.GregorianCalendar;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

/**
 * Annuncio di un utente del sistema che vuole effttuare un ordine verso un certo sito
 * e condividere le spese di spedizione con altri utenti che possono iscriversi all'annuncio
 * @author ale
 */
@Entity
public class Annuncio implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    /**
     * Sito a cui si riferisce l'annuncio
     */
    @ManyToOne
    protected SitoAcquisti sito;
    /**
     * Indirizzo a cui gli iscritti all'annuncio potranno ritirare i propri articoli
     */
    @OneToOne
    protected IndirizzoAbitazione indirizzoRitiro;
    /**
     * Adesioni per questo annuncio
     */
    @OneToMany(mappedBy = "annuncio")
    protected List<Adesione> adesioni;
    /**
     * Creatore dell'annuncio
     */
    @ManyToOne
    @JoinColumn(name="creatoreAnnuncio")
    protected UtenteRegistrato creatoreAnnuncio;
    /**
     * Data di chiusura dell'annuncio (data oltre la quale non si possono più effettuare iscrizioni né cancellare l'annuncio)
     */
    @Temporal(TemporalType.DATE)
    protected GregorianCalendar dataChiusura;
    /**
     * Data di ordine dell'annuncio (data entro la quale gli iscritti sono tenuti a pagare i propri articoli)
     */
    @Temporal(TemporalType.DATE)
    protected GregorianCalendar dataOrdine;
    /**
     * Spesa del creatore per questo sito; impostata solo se il sito prevede la spedizione gratuita a fronte di una certa spesa minima
     */
    protected float spesaCreatore=0;
    /**
     * Costo della spedizione dal sito verso il creatore dell'annuncio
     */
    protected float costoSpedizione;
    /**
     * Spesa minima oltre la quale la spedizione dal sito è gratuita; impostata solo se il sito prevede la spedizione gratuita a fronte di una certa spesa minima
     */
    protected float spesaMinSpedGratis;
    /**
     * true sse il sito prevede la spedizione gratuita a fronte di una certa spesa minima
     */
    protected boolean supportaSpedGratis;

    /**
     * Recupera il costo della spedizione dal sito verso il creatore dell'annuncio
     * @return il costo della spedizione dal sito verso il creatore dell'annuncio
     */
    public float getCostoSpedizione() {
        return costoSpedizione;
    }

    /**
     * Imposta il costo della spedizione dal sito verso il creatore dell'annuncio
     * @param costoSpedizione il costo della spedizione dal sito verso il creatore dell'annuncio
     */
    public void setCostoSpedizione(float costoSpedizione) {
        this.costoSpedizione = costoSpedizione;
    }

    /**
     * Recupera la spesa minima oltre la quale il sito prevede la spedizione gratuita
     * @return la spesa minima oltre la quale il sito prevede la spedizione gratuita
     */
    public float getSpesaMinSpedGratis() {
        return spesaMinSpedGratis;
    }

    /**
     * Imposta la spesa minima oltre la quale il sito prevede la spedizione gratuita
     * @param spesaMinSpedGratis la spesa minima oltre la quale il sito prevede la spedizione gratuita
     */
    public void setSpesaMinSpedGratis(float spesaMinSpedGratis) {
        this.spesaMinSpedGratis = spesaMinSpedGratis;
    }

    /**
     * Verifica se il sito prevede la spedizione gratuita a fronte di una certa spesa minima
     * @return true sse il sito prevede la spedizione gratuita a fronte di una certa spesa minima
     */
    public boolean isSupportaSpedGratis() {
        return supportaSpedGratis;
    }

    /**
     * Definisce se il sito prevede la spedizione gratuita a fronte di una certa spesa minima
     * @param supportaSpedGratis true sse il sito prevede la spedizione gratuita a fronte di una certa spesa minima
     */
    public void setSupportaSpedGratis(boolean supportaSpedGratis) {
        this.supportaSpedGratis = supportaSpedGratis;
    }
    
    /**
     * Restituisce il numero di iscritti la cui adesione è stata accettata dal creatore dell'annuncio
     * @return il numero di iscritti la cui adesione è stata accettata dal creatore dell'annuncio
     */
    public int getNumIscrittiAccettati(){
        int nIscritti = 0;
        for (Adesione a: adesioni){
            if (a.stato == Adesione.StatoAdesione.ACCETTATA) nIscritti++;
        }
        return nIscritti;
    }
    
    /**
     * Restituisce la spesa del creatore verso il sito a cui l'annunciosi riferisce
     * @return la spesa del creatore verso il sito a cui l'annunciosi riferisce
     */
    public float getSpesaCreatore() {
        return spesaCreatore;
    }

    /**
     * Imposta la spesa del creatore verso il sito a cui l'annunciosi riferisce
     * @param spesaCreatore la spesa del creatore verso il sito a cui l'annunciosi riferisce
     */
    public void setSpesaCreatore(float spesaCreatore) {
        this.spesaCreatore = spesaCreatore;
    }

    /**
     * Verifica se un utente è iscritto a questo annuncio
     * @param utente utente da cercare tra gli iscritti
     * @return l'adesione che lega l'utente a questo annuncio, se è iscritto; null altrimenti
     */
    public Adesione isIscritto(UtenteRegistrato utente){
        for (Adesione a: adesioni){
            if (a.getIscritto().equals(utente)) return a;
        }
        return null;
    }
    
    /**
     * Calcola il costo della spedizione per un iscritto. 
     * Ad esempio, se il costo della spedizione è 100€ e ci sono tre iscritti accettati,
     * la spedizione è divisa per 4 (3 iscritti più il creatore) e l'iscritto spede 26€
     * (25€ per la spedizione più 1€ di contributo al creatore)
     * Se si è raggiunta la soglia di spedizione gratuita restituisce 1, cioè il solo contributo al creatore.
     * @return il costo della spedizione per un iscritto
     */
    public float getCostoSpedizionePerIscritto(){
        if (getPrezzoRaggiunto() >= spesaMinSpedGratis) return 1; //il contributo al creatore
              
        return (getCostoSpedizione()/(getNumIscrittiAccettati()+1))+1; 
    }
    
    /**
     * Calcola il costo della spedizione per il creatore dell'annuncio. 
     * Ad esempio, se il costo della spedizione è 100€ e ci sono tre iscritti accettati,
     * la spedizione è divisa per 4 (3 iscritti più il creatore) e il creatore spende 22€
     * (25€ per la spedizione meno 3€ di contributi da parte degli iscritti)
     * Se si è raggiunta la soglia di spedizione gratuita, restituisce 0.
     * @return il costo della spedizione per il creatore dell'annuncio.
     */
    public float getCostoSpedizionePerCreatore(){
        if (getPrezzoRaggiunto() >= spesaMinSpedGratis) return 0;
              
        return (getCostoSpedizione()/(getNumIscrittiAccettati()+1))-getNumIscrittiAccettati(); 
    }
    
    /**
     * Calcola il costo della spedizione per un utente non iscritto, se questo si iscrivesse. 
     * Ad esempio, se il costo della spedizione è 100€ e ci sono tre iscritti accettati,
     * la spedizione è divisa per 5 (3 iscritti più il creatore, più l'utente se si iscrivesse) 
     * e il nuovo iscritto spende 21€ (20€ per la spedizione più 1€ di contributo al creatore)
     * Se si è raggiunta la soglia di spedizione gratuita, restituisce 0.
     * @return il costo della spedizione per un utente non iscritto, se questo si iscrivesse. 
     */
    public float getCostoSpedizionePerAltri(){
        if (getPrezzoRaggiunto() >= spesaMinSpedGratis) return 1;
              
        return (getCostoSpedizione()/(getNumIscrittiAccettati()+1+1))+1; //aggiungo 1 perché anche il creatore condivide le spese, un altro 1 per far vedere le spese che pagherebbe chi si iscrive
    }
    
    /**
     * Calcola la spesa ancora necessaria per raggiungere la soglia di spedizione gratuita; 
     * restituisce 0 se il sito non prevede la spedizione gratuita
     * @return la spesa ancora necessaria per raggiungere la soglia di spedizione gratuita, 
     * 0 se il sito non prevede la spedizione gratuita
     */
    public float getMancaASpedGratis(){
        if (!supportaSpedGratis) return 0;
        else {
            float manca = spesaMinSpedGratis-getPrezzoRaggiunto();
            if (manca > 0) return manca;
            return 0;
        }
    }
    
    /**
     * Calcola la spesa totale raggiunta come somma della spesa di tutte le adesioni accettate, più la spesa del creatore
     *
     * @return la spesa totale raggiunta
     */
    public float getPrezzoRaggiunto() {
        float prezzo = 0;
        for (Adesione a: adesioni){
            if (a.stato == Adesione.StatoAdesione.ACCETTATA){
                for (Articolo art: a.getArticoli()){
                    if (art.getStato() == Articolo.Stato.ACCETTATO){
                        prezzo += art.getPrezzo();
                    }
                }
            }
        }
        return prezzo+spesaCreatore;
    }

    /**
     * Get the value of dataOrdine
     *
     * @return the value of dataOrdine
     */
    public GregorianCalendar getDataOrdine() {
        return dataOrdine;
    }

    /**
     * Set the value of dataOrdine
     *
     * @param dataOrdine new value of dataOrdine
     */
    public void setDataOrdine(GregorianCalendar dataOrdine) {
        this.dataOrdine = dataOrdine;
    }

    /**
     * Get the value of dataChiusura
     *
     * @return the value of dataChiusura
     */
    public GregorianCalendar getDataChiusura() {
        return dataChiusura;
    }

    /**
     * Set the value of dataChiusura
     *
     * @param dataChiusura new value of dataChiusura
     */
    public void setDataChiusura(GregorianCalendar dataChiusura) {
        this.dataChiusura = dataChiusura;
    }

    /**
     * Verifica se l'annuncio è chiuso 
     * (data odierna maggiore o uguale a quella di chisura dell'annuncio oppure il creatore ha chiuso l'annuncio anticipatamente)
     * @return true sse l'annuncio è chiuso
     */
    public boolean isChiuso(){
        GregorianCalendar today = new GregorianCalendar();
        return !today.before(dataChiusura);
    }
    
    /**
     * Verifica se è il momento di effettuare l'ordine
     * (data odierna maggiore o uguale a quella di ordine dell'annuncio)
     * @return true sse è il momento di effettuare l'ordine
     */
    public boolean isOrdinato(){
        GregorianCalendar today = new GregorianCalendar();
        return !today.before(dataOrdine);
    }
    
    /**
     * Get the value of creatore
     *
     * @return the value of creatore
     */
    public UtenteRegistrato getCreatoreAnnuncio() {
        return creatoreAnnuncio;
    }

    /**
     * Set the value of creatore
     *
     * @param creatore new value of creatore
     */
    public void setCreatoreAnnuncio(UtenteRegistrato creatore) {
        this.creatoreAnnuncio = creatore;
    }

    /**
     * Get the value of adesioni
     *
     * @return the value of adesioni
     */
    public List<Adesione> getAdesioni() {
        return adesioni;
    }

    /**
     * Set the value of adesioni
     *
     * @param adesioni new value of adesioni
     */
    public void setAdesioni(List<Adesione> adesioni) {
        this.adesioni = adesioni;
    }

    /**
     * Get the value of indirizzoRitiro
     *
     * @return the value of indirizzoRitiro
     */
    public IndirizzoAbitazione getIndirizzoRitiro() {
        return indirizzoRitiro;
    }

    /**
     * Set the value of indirizzoRitiro
     *
     * @param indirizzoRitiro new value of indirizzoRitiro
     */
    public void setIndirizzoRitiro(IndirizzoAbitazione indirizzoRitiro) {
        this.indirizzoRitiro = indirizzoRitiro;
    }

    /**
     * Get the value of sito
     *
     * @return the value of sito
     */
    public SitoAcquisti getSito() {
        return sito;
    }

    /**
     * Set the value of sito
     *
     * @param sito new value of sito
     */
    public void setSito(SitoAcquisti sito) {
        this.sito = sito;
    }

    /**
     * Restituisce l'id dell'annuncio
     * @return l'id dell'annuncio
     */
    public Long getId() {
        return id;
    }

    /**
     * Imposta l'id dell'annuncio
     * @param id l'id dell'annuncio
     */
    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Annuncio)) {
            return false;
        }
        Annuncio other = (Annuncio) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "Annuncio{" + "id=" + id + ", sito=" + sito + ", indirizzoRitiro=" + indirizzoRitiro + ", adesioni=" + adesioni + ", creatoreAnnuncio=" + creatoreAnnuncio + ", dataChiusura=" + dataChiusura + ", dataOrdine=" + dataOrdine + ", spesaCreatore=" + spesaCreatore + '}';
    }
     
}
